package common.extractText;


import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Pattern;

public class ReadTxt {

    static String tableName = "pdf.pmid_pdf_sentence"; // 保存的表名
    static String connector = "ㅠ⫅ㅛ".toLowerCase(); // 连接符-
    static String paragraph = "ㅑ╉╧".toLowerCase(); // 段落开始符
    static String subProject = "ㅠ⨦ⱔ".toLowerCase(); // 子段落开始符
    static String lineBreak = "⬆เ⎨".toLowerCase(); // 换行符号，文本加入保存时，前面均要加上这个符号
    static String lastStr = "∱≱⬇".toLowerCase(); // 整个文档的结束符号
    static String mostStr = "⧄⬧⧁".toLowerCase(); // 最多的字符串的标识符号

    public static List<String> readTxt(List<String> split, Set<String> mostStrSet) {
        List<String> strList = new ArrayList<>();
        StringBuilder result = new StringBuilder();
        getIsStrType(split, mostStrSet); // 是文本型的，还是空格型的（一行文本一行文本），现在不需要这个功能了，但确定最多的字符串，即页眉页脚，还是需要的。
//        isStrType = true;
        Set<String> paragraphSet = Paragraph.getParagraph(); // 段落标记字符的集合
        int lineLen = getLineLen(split); // 平均一满行的字符串个数。
        boolean isReferences = false; // 这个参数的意思是，当出现参文这个标识符之后，直到出现参文结束标志之前，全部认定为参文。
        strList.add(paragraph + "Title");
//        System.out.println("isStrType: " + isStrType + " lineLen: " + lineLen);
        boolean isEndStop = false; // 上一句的结束是否是句号
        for (int i = 0; i < split.size(); i++) {
            String next = (split.size() > i + 1) ? split.get(i + 1).trim() : ""; // 第一行为序号加段落的名称，第二行（即下一行）可能还是段落的名称。
            String next2Nd = (split.size() > i + 2) ? split.get(i + 2).trim() : ""; // 下一行的下一行
            String s = split.get(i).replace("A B S T R A C T", "Abstract").replace("a b s t r a c t", "Abstract");
            if (s.trim().length() > 0 || isReferences) {
                // Abstract. This study considers 处理类似这种的，把Abstract提出来加上段落标示。
                String t = s.trim();
//                System.out.println("-0.0 t: " + t + "| next: |" + next + "|");
                if (t.equals("Abstract") || t.startsWith("Abstract. ") || t.startsWith("[Abstract] ") || t.startsWith("Abstract—") || t.startsWith("Abstract: ")) {
//                    System.out.println("-0.1 t: " + t + "| next: |" + next + "|");
//                    System.out.println("-5.0 isEndStop: " + isEndStop + " s: " + s);
                    result = addResultList(result, strList);
                    String end = t.equals("Abstract") ? "" : "";
                    end = t.startsWith("Abstract. ") ? ". " : end;
                    end = t.startsWith("Abstract—") ? "—" : end;
                    end = t.startsWith("Abstract: ") ? ": " : end;
                    end = t.startsWith("[Abstract] ") ? "  " : end;
                    strList.add(paragraph + "Abstract");
                    isReferences = false;
                    result.append(lineBreak).append(t.substring(("Abstract" + end).length())).append(" ");
                    continue;
                }
                if (t.equals("a r t i c l e i n f o") || t.equals("A R T I C L E  I N F O")) {
                    result = addResultList(result, strList);
                    strList.add(paragraph + "articleinfo");
                    isReferences = false;
                    continue;
                }
                if (t.toLowerCase().equals("reference") || t.toLowerCase().equals("reference:") || t.toLowerCase().equals("references") || t.toLowerCase().equals("references:") || t.toLowerCase().equals("bibliografía") || t.toLowerCase().equals("references 1") ||
                        t.toLowerCase().equals("références") || t.toLowerCase().equals("références:") || t.toLowerCase().equals("references and recommended reading") || (t.equals("Notes") && next.equals("") && Pattern.compile("^1\\. [A-Z]").matcher(next2Nd.trim()).find())) {
//                    System.out.println("-6.0 isEndStop: " + isEndStop + " s: " + s);
//                    System.out.println("-0.2 t: " + t + "| next: |" + next + "|");
                    result = addResultList(result, strList);
                    isReferences = true;
                    strList.add(paragraph + "references");
                    continue;
                }
                if (isReferences && isReferencesEnd(s, next)) {
//                    System.out.println("-7.0 isEndStop: " + isEndStop + " s: " + s);
                    result = addResultList(result, strList); // 如果遇到了参文的结束标识，则把之前的先保存，再添加段落名称："other"，然后再把结束标识的文本保存，并结束参文。
                    strList.add(paragraph + "Other");
//                    result.append(lineBreak).append(s).append(" ");
                    isReferences = false;
                }

                if (fuHeParagraph(s) && Paragraph.isParagraph(s, paragraphSet) && !endStr(result.toString())) { // 如果是段落，并且不是指定的字符串结尾。
//                    System.out.println("-0.3 t: " + t + "| next: |" + next + "|");
                    result = addResultList(result, strList); // 如果遇到段落或小段落，则先把原来的值给保存。
                    strList.add(paragraph + s);
                    if (!s.contains("references")) {
                        isReferences = false;
                    }
                    continue;
                }

                if (mostStrSet.contains(s.trim())) { // 如果是出现最多的字符串，则给加上最多的标识。
                    strList.add(mostStr + s.trim());
                    continue;
                }

                // 这种是处理段落的序号在一行，内容又另起了一行的情况，而且有些内容还不止一行，有三行甚至四行的情况
                boolean isSubProject = Pattern.compile("^[0-9]\\.[0-9]\\.$").matcher(s.trim()).find(); // subProject是小段落的名称，即一般为1.1. 之类的小标号。
                if (fuHeParagraph(s) && isEndStop && !isReferences) { // 如果上一句是以句号结束，并且不是参文
//                    System.out.println("-0.4 t: " + t + "| next: |" + next + "|");
                    if (Pattern.compile("^[0-9]\\.$").matcher(s.trim()).find() || isSubProject) {
//                        System.out.println("-1.0 isEndStop: " + isEndStop + " s: " + s);
                        String next2 = (split.size() > i + 2) ? split.get(i + 2).trim() : "";
                        String next3 = (split.size() > i + 3) ? split.get(i + 3).trim() : "";
                        String next4 = (split.size() > i + 4) ? split.get(i + 4).trim() : "";
                        if (next.length() > 1 && Pattern.compile("^[A-Z][a-z]").matcher(next).find()) {
                            if (next2.length() < 1) { // 如果下下行，即第一行为序号，第二行为段落名称，第三行为空。
                                result = addResultList(result, strList); // 如果遇到段落或小段落，则先把原来的值给保存。
                                if (isSubProject) {
                                    strList.add(subProject + s + next);
                                } else {
                                    strList.add(paragraph + s + next);
                                    isReferences = false;
                                }
                                i++;
                                continue;
                            } else {
                                if (next3.length() < 1) { // 如果下下下行，即第一行为序号，第二行为段落名称，第三行还有文本，而第四行没有文本了。
                                    result = addResultList(result, strList);
                                    if (isSubProject) {
                                        strList.add(subProject + s + next + next2);
                                    } else {
                                        strList.add(paragraph + s + next + next2);
                                        isReferences = false;
                                    }
                                    i = i + 2;
                                    continue;
                                } else if (next4.length() < 1) { // 如果下下下下行，即第一行为序号，第二行为段落名称，第三行还有文本，第四行还有文本，而第五行没有文本了。
                                    result = addResultList(result, strList);
                                    if (isSubProject) {
                                        strList.add(subProject + s + next + next2 + next3);
                                    } else {
                                        strList.add(paragraph + s + next + next2 + next3);
                                        isReferences = false;
                                    }
                                    i = i + 3;
                                    continue;
                                }
                            }
                        }
                    }
                }

                // 这种情况是序号与段落的名称在同一行，或者一行没有结束又另起了一行。理论上可能会另起一行，也可能另起二行或更多，这里不考虑那么多，只考虑最多二行。或者本行全部由大写字符构成且长度大于5位，可以认定为段落的名称
//                System.out.println("isReferences: " + isReferences + " s: |" + s + "| next: |" + next + "|");
                if (fuHeParagraph(s) && !isReferences && (Pattern.compile("^[0-9]\\. [A-Z]").matcher(s.trim()).find() || Pattern.compile("^[0-9] [A-Z ]$").matcher(s.trim()).find() ||
                        Pattern.compile("^[0-9]\\. [0-9][A-Z]").matcher(s.trim()).find() || Pattern.compile("^[0-9] [0-9][A-Z ]$").matcher(s.trim()).find() ||
                        (Pattern.compile("^[A-Z ]+$").matcher(s.trim()).find() && s.trim().length() > 5) || Pattern.compile("^[0-9][A-Z ]+$").matcher(s.trim()).find())) {
//                    System.out.println("-0.5 t: " + t + "| next: |" + next + "|");
//                    System.out.println("-2.0 isEndStop: " + isEndStop + " s: |" + s + "| next: |" + next + "| lineLen: " + lineLen + " s.length(): " + s.length());
                    if (fuHeParagraph(next) && Pattern.compile("^[A-Z ]+$").matcher(next.trim()).find()) { // 如果下一行还继续全部是大写，则把下一行与本行合并，这个不能当一个参数，在下面使用，应优先在这里处理。
//                        System.out.println("-2.1 isEndStop: " + isEndStop + " s: |" + s + "| next: |" + next + "| lineLen: " + lineLen + " s.length(): " + s.length());
                        result = addResultList(result, strList);
                        strList.add(paragraph + s + " " + next);
                        isReferences = false;
                        i++;
                        continue;
                    }
                    if (strList.get(strList.size() - 1).contains(paragraph)) { // 理论上上一行是段落，本行就不允许是段落了，即使判断为段落，也要按普通文本来处理。
//                        System.out.println("-2.2 isEndStop: " + isEndStop + " s: |" + s + "| next: |" + next + "| lineLen: " + lineLen + " s.length(): " + s.length());
                        result.append(lineBreak).append(s).append(" ");
                        continue;
                    }
                    if (s.length() < lineLen) {
//                        System.out.println("-2.3 isEndStop: " + isEndStop + " s: |" + s + "| next: |" + next + "| lineLen: " + lineLen + " s.length(): " + s.length());
                        result = addResultList(result, strList);
                        strList.add(paragraph + s);
                        isReferences = false;
                        continue;
                        // 如果下一行还继续全部是大写，则把下一行与本行合并。如果下一行不是数字开头，且不满行，也按是上一行的延续处理。
                    } else if (fuHeParagraph(next) && next.length() > 1 && !Pattern.compile("^[0-9]").matcher(next).find() && next.length() < lineLen) {
//                        System.out.println("-2.4 isEndStop: " + isEndStop + " s: |" + s + "| next: |" + next + "| lineLen: " + lineLen + " s.length(): " + s.length());
                        result = addResultList(result, strList);
                        strList.add(paragraph + s + next);
                        isReferences = false;
                        i++;
                        continue;
                    } else {
//                        System.out.println("-2.5 isEndStop: " + isEndStop + " s: |" + s + "| next: |" + next + "| lineLen: " + lineLen + " s.length(): " + s.length());
                        result.append(lineBreak).append(s).append(" ");
                    }
                }

                // 这一种是段落的子段落名称，即小段落，小段落的序号与小段落的名称在同一行，最多只考虑两行。前提是不能是参文
                if (fuHeParagraph(s) && !isReferences && (Pattern.compile("^[0-9]\\.[0-9]\\. [A-Z]").matcher(s.trim()).find() || Pattern.compile("^[0-9]\\.[0-9] [0-9][A-Z]").matcher(s.trim()).find() ||
                        Pattern.compile("^[0-9]\\.[0-9]\\. [0-9][A-Z]").matcher(s.trim()).find() || Pattern.compile("^[0-9]\\.[0-9] [A-Z]").matcher(s.trim()).find())) {
//                    System.out.println("-0.6 t: " + t + "| next: |" + next + "|");
//                    System.out.println("--3.0 isEndStop: " + isEndStop + " s: |" + s + "| next: |" + next + "| lineLen: " + lineLen + " s.length(): " + s.length());
                    String next2 = (split.size() > i + 2) ? split.get(i + 2).trim() : "";
                    if (Pattern.compile("^[A-Z ]+$").matcher(next).find()) { // 如果下一行还继续全部是大写，则把下一行与本行合并
//                        System.out.println("--3.1 isEndStop: " + isEndStop + " s: |" + s + "| next: |" + next + "| lineLen: " + lineLen + " s.length(): " + s.length());
                        result = addResultList(result, strList);
                        strList.add(subProject + s + " " + next);
                        i++;
                        continue;
                    }
                    if (s.length() < lineLen) {
//                        System.out.println("--3.3 isEndStop: " + isEndStop + " s: |" + s + "| next: |" + next + "| lineLen: " + lineLen + " s.length(): " + s.length());
                        result = addResultList(result, strList);
                        strList.add(subProject + s);
                        continue;
                        // 如果下一行还继续全部是大写，则把下一行与本行合并。如果下一行不是数字开头，且不满行，也按是上一行的延续处理。||Pattern.compile("^[A-Z ]+$").matcher(next).find()
                    } else if (next.length() > 1 && !Pattern.compile("^[0-9]").matcher(next).find() && next.length() < lineLen) {
//                        System.out.println("--3.4 isEndStop: " + isEndStop + " s: |" + s + "| next: |" + next + "| lineLen: " + lineLen + " s.length(): " + s.length());
                        result = addResultList(result, strList);
                        strList.add(subProject + s + next);
                        i++;
                        continue;
                    } else {
//                        System.out.println("--3.5 isEndStop: " + isEndStop + " s: |" + s + "| next: |" + next + "| lineLen: " + lineLen + " s.length(): " + s.length());
                        result.append(lineBreak).append(s).append(" ");
                    }
                }
                result.append(lineBreak).append(s).append(" ");
                isEndStop = s.trim().endsWith(".") || Pattern.compile("^[0-9]+ of [0-9]+$").matcher(s.trim()).find();
            }
        }
        addResultList(result, strList);
        return strList;
    }

    // 如果是一些不合适的单词或符号结尾，返回true。
    public static boolean endStr(String str) {
        String e = str.toLowerCase().trim();
        return e.endsWith(" the") || e.endsWith(" in") || e.endsWith(" on") || e.endsWith(" of") || e.endsWith(" and") || e.endsWith(",") || e.endsWith(":") || e.endsWith("-") || e.endsWith("(") ||
                e.endsWith(" by") || e.endsWith(" to") || e.endsWith(" as") || e.endsWith(" with") || e.endsWith(" from") || e.endsWith(" is") || e.endsWith(" not") || e.endsWith(" but") || e.endsWith(" so") || e.endsWith(" if") ||
                e.endsWith(" for") || e.endsWith(" above") || e.endsWith(" inside") || e.endsWith(" outside") || e.endsWith(" onto") || e.endsWith(" within") || e.endsWith(" about") || e.endsWith(" across") ||
                e.endsWith(" against") || e.endsWith(" among") || e.endsWith(" after") || e.endsWith(" behind") || e.endsWith(" besides") || e.endsWith(" beyond") || e.endsWith(" concerning") || e.endsWith(" beneath") ||
                e.endsWith(" between") || e.endsWith(" despite") || e.endsWith(" exy");
    }

    // 判断是否符合段落的名称规范，一般来说，连续相同4个字母的单词，或者长度大于45位的单词，我们认为是不符合的。
    public static boolean fuHeParagraph(String str) {
        String[] split = str.replaceAll(" +", " ").split("");
        int n = 0;
        for (int i = 0; i < split.length; i++) {
            String s = split[i];
            String next = (split.length > i + 1) ? split[i + 1].trim() : "";
            n = s.equals(next) ? n + 1 : 0;
            if (n > 1) { // 大于1即需要至少连接的相同的3个字符，才可以
                return false;
            }
        }
        n = 0;
        String[] list = str.replaceAll(" +", " ").split(" ");
        for (String s : list) {
            n = s.length() == 1 ? n + 1 : 0;
//            System.out.println(s + " n: " + n);
            if (n > 2) { // 大于2即需要至少连接的相同的3个长度为1的字符，才可以
                return false;
            }
            if (s.length() > 26) { // 据查目前最长的单词是45个，如果长于这个，就认为不是一个单词，而可能是表示基因的连续字符串，而这种的，自然不是什么有意义的词了。
                return false;
            }
        }
        // 如果段落内含有年份和起至页码，也不认为是段落
        if (Pattern.compile(", [0-9]+\\–[0-9]+\\(197|198|199|200|201|202[0-9]\\)\\.").matcher(str).find()) {
            return false;
        }
        return !Pattern.compile(" \\(197|198|199|200|201|202[0-9]\\)\\. ").matcher(str).find() || !Pattern.compile(", [0-9]+\\–[0-9]+\\. ").matcher(str).find();
    }

    // 判断参文是否结束了，如果结束了，返回true。t.contains(" department of ") || (t.endsWith(" department") && next.equals("of")) || 原来是有部门，现给予取消。
    public static boolean isReferencesEnd(String first, String next) {
        String t = first.toLowerCase().trim();
        return Pattern.compile("^figure [0-9]+\\. ").matcher(t).find() || Pattern.compile("^table [0-9]+\\. ").matcher(t).find() || Pattern.compile("^[A-Z ]+$").matcher(t).find() || t.startsWith("disclaimer: ") ||
                t.startsWith("figure legends ") || t.startsWith("conflict of interest: ") || t.startsWith("publisher’s note") || t.startsWith("acknowledgements") || t.startsWith("international journal of general medicine dovepress") ||
                t.startsWith("author contributions") || t.startsWith("supplementary materials") || Pattern.compile("^table [0-9]+ ").matcher(t).find() || Pattern.compile("^tables [0-9]+$").matcher(t).find() ||
                t.startsWith("publish your work in this journal") || t.startsWith("publisher's note") || Pattern.compile("^& figure [0-9]+\\. ").matcher(t).find() ||
                t.startsWith("open access") || t.startsWith("graphical abstract") || t.startsWith("aasssaa");
    }

    public static StringBuilder addResultList(StringBuilder result, List<String> resultList) {
        if (result.toString().trim().length() > 0) {
            resultList.add(result.toString().replaceAll(" +", " "));
        }
        return new StringBuilder();
    }

    // 判断有文本的行数和无文本的行数，如果有文本的行数是无文本的行数的1.25倍以上，则认为是文本型的，若不是则认为是一行文本一行空格的空格型的。
    public static void getIsStrType(List<String> strList, Set<String> mostStrSet) {
        Map<String, Integer> mostStrMap = new ConcurrentHashMap<>();
//        int space = 0; // 空格
//        int isStr = 0; // 有字符
        for (String s : strList) {
//            if (s.trim().length() > 1) {
//                isStr++;
//            } else {
//                space++;
//            }
            if (s.contains(". ") || Pattern.compile("^[A-Z ]+$").matcher(s).find()) {
                mostStrMap.compute(s.trim(), (key, value) -> value != null ? (value + 1) : 1);
            }
        }
        Map<String, Integer> sortMap = Tools.strMapSortValueInt(mostStrMap, 2);
//        Integer most = sortMap.entrySet().iterator().next().getValue(); // 取出LinkedHashMap第一个元素的值，即最大的那个，如果第二个或以后的也大于或等于此值，均加入最多的mostStrSet集合中。
        for (Map.Entry<String, Integer> entry : sortMap.entrySet()) {
            if (entry.getValue() > 3) {
                mostStrSet.add(entry.getKey());
            }
        }
//        System.out.println("");
//        return Tools.Arith.div(isStr, space == 0 ? 1 : space) > 1.25;
    }

    // 一般一行字符串的长度，如果是满行的，基本都在50上下，因此，以30作为最短值，计算大于30的行数，再求平均值，最后以这个平均值作为是否满行的标准。
    public static int getLineLen(List<String> split) {
        int sum = 0; // 长度大于30的各行的总长度
        int len = 0; // 长度大于30的行数
        for (String s : split) {
            if (s.length() > 30) {
                len++;
                sum += s.length();
            }
        }
        int i = sum / (len == 0 ? 1 : len);
        if (i > 30) {
            return i - (i / 15); // 返回平均值再少10%，因为一行字符串，可能稍长一些，也可能稍短一些
        }
        return 45;
    }


}
